关于ReentrantLock
从字面意思可以看出,ReentrantLock是可重入锁的意思,当然,其实Synchronized也是可重入锁,两者都是同一个线程每进入一次,锁的计算器都自增1,所以要等到锁的计数器为0时才能释放锁。
ReentrantLock最常见的使用方式
1 2 3 4 5 6 7
| ReentrantLock lock = new ReentrantLock(); lock.lock(); try { ... } finally { lock.unlock(); }
|
正如上面这段代码所示,在使用ReentrantLock时需要手动去加锁和解锁,这是因为ReentrantLock是基于JDK实现的,而我们最常使用的锁Synchronized是依赖于JVM来实现的,由JVM帮我们完成解锁操作。
ReentrantLock代码分析
阅读源码最好的方法就是从某个常用方法进入,比如我们在使用ReentrantLock时最常使用的就是其lock()方法,我们看下ReentrantLock的lock()方法:
1 2 3
| public void lock() { sync.lock(); }
|
lock()方法里的代码很简短,这里出现了一个变量sync,那么它是什么意思呢?在ReentrantLock中其定义为:
1
| private final Sync sync;
|
其为一个Sync类型的常量,我们继续看下Sync类是什么?
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47
| abstract static class Sync extends AbstractQueuedSynchronizer { private static final long serialVersionUID = -5179523762034025860L; * Performs {@link Lock#lock}. The main reason for subclassing * is to allow fast path for nonfair version. */ abstract void lock(); * Performs non-fair tryLock. tryAcquire is implemented in * subclasses, but both need nonfair try for trylock method. */ final boolean nonfairTryAcquire(int acquires) { final Thread current = Thread.currentThread(); int c = getState(); if (c == 0) { if (compareAndSetState(0, acquires)) { setExclusiveOwnerThread(current); return true; } } else if (current == getExclusiveOwnerThread()) { int nextc = c + acquires; if (nextc < 0) throw new Error("Maximum lock count exceeded"); setState(nextc); return true; } return false; } protected final boolean tryRelease(int releases) { int c = getState() - releases; if (Thread.currentThread() != getExclusiveOwnerThread()) throw new IllegalMonitorStateException(); boolean free = false; if (c == 0) { free = true; setExclusiveOwnerThread(null); } setState(c); return free; } ... }
|
Sync是ReentrantLock的静态抽象内部类,其继承自AbstractQueuedSynchronizer,AbstractQueuedSynchronizer也就是AQS,我们现在不去分析它,只需要知道其内部存在一个获取锁的等待队列及其互斥锁状态的int状态位(0表示当前没有线程持有该锁,n表示存在某线程重入锁n次)。
正如上面所述,Sync是抽象类,在ReentrantLock中其具有两个实现类,分别为NonfairSync和FairSync,也就是非公平锁和公平锁。
ReentrantLock的lock()方法调用的是Sync类的lock()方法,而Sync类的lock()方法是一个抽象方法,其具体实现在NonfairSync和FairSync中,在NonfairSync类中lock()方法的实现为:
1 2 3 4 5 6 7 8
| final void lock() { if (compareAndSetState(0, 1)) setExclusiveOwnerThread(Thread.currentThread()); else acquire(1); }
|
而在FairSync类中lock()方法的实现为:
1 2 3
| final void lock() { acquire(1); }
|
可见两者的lock()方法中都涉及到了一个acquire()函数,而acquire()方法的具体实现在Sync类的父类也即AbstractQueuedSynchronizer中:
1 2 3 4 5
| public final void acquire(int arg) { if (!tryAcquire(arg) && acquireQueued(addWaiter(Node.EXCLUSIVE), arg)) selfInterrupt(); }
|
通过tryAcquire()方法试图获取锁,获取到直接返回结果,否则通过嵌套调用acquireQueued()和addWaiter()方法将请求获取锁的线程加入等待队列。
tryAcquire()方法在AbstractQueuedSynchronizer抽象类中的实现为:
1 2 3
| protected boolean tryAcquire(int arg) { throw new UnsupportedOperationException(); }
|
发现在AbstractQueuedSynchronizer类的tryAcquire()方法中直接抛出了异常,事实上tryAcquire()方法在FairSync和NonfairSync两个实现类中均有实现,我们看下NonfairSync类中的tryAcquire()方法吧:
1 2 3
| protected final boolean tryAcquire(int acquires) { return nonfairTryAcquire(acquires); }
|
发现在NonfairSync类中的tryAcquire()方法中直接调用了nonfairTryAcquire()方法,而这个方法在其父类即Sync类中已经实现:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| final boolean nonfairTryAcquire(int acquires) { final Thread current = Thread.currentThread(); int c = getState(); if (c == 0) { if (compareAndSetState(0, acquires)) { setExclusiveOwnerThread(current); return true; } } else if (current == getExclusiveOwnerThread()) { int nextc = c + acquires; if (nextc < 0) throw new Error("Maximum lock count exceeded"); setState(nextc); return true; } return false; }
|
上来先判断锁的状态,若为0,则表示锁还未被占用,则直接通过CAS来抢占,抢占成功,返回true,反之,若不为0,则表示锁已经被占用,此时判断锁的持有者线程是否为当前线程,若是,则通过累加状态标识重入次数。
抢占不成功或者锁的本身持有者不是当前线程,则返回false,继而后续通过进入等待队列的方式排队获取锁。
至于FairSync类中的tryAcquire()这里就不加以介绍了,感兴趣的可以自己去阅读以下,其实差不多。
默认实现
ReentrantLock的默认实现为非公平锁:
1 2 3
| public ReentrantLock() { sync = new NonfairSync(); }
|
你也可以通过另外一个构造方法执行锁的实现方式:
1 2 3
| public ReentrantLock(boolean fair) { sync = fair ? new FairSync() : new NonfairSync(); }
|
说明
本文参考了文章Java并发之ReentrantLock详解。